/*
 * Copyright (C) 2010-2011 ProjectSkyfire <http://www.projectskyfire.org/>
 * 
 * Copyright (C) 2011 ArkCORE <http://www.arkania.net/>
 * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Scripts for spells with SPELLFAMILY_ROGUE and SPELLFAMILY_GENERIC spells used by rogue players.
 * Ordered alphabetically using scriptname.
 * Scriptnames of files in this file should be prefixed with "spell_rog_".
 */

#include "ScriptPCH.h"
#include "SpellAuraEffects.h"

enum RogueSpells {
	ROGUE_SPELL_SHIV_TRIGGERED = 5940,
	ROGUE_SPELL_GLYPH_OF_PREPARATION = 56819,
	ROGUE_SPELL_PREY_ON_THE_WEAK = 58670,
};

// Cheat Death
class spell_rog_cheat_death: public SpellScriptLoader {
public:
	spell_rog_cheat_death() :
			SpellScriptLoader("spell_rog_cheat_death") {
	}

	class spell_rog_cheat_death_AuraScript: public AuraScript {
		PrepareAuraScript(spell_rog_cheat_death_AuraScript)
		;

		uint32 absorbChance;

		enum Spell {
			ROG_SPELL_CHEAT_DEATH_COOLDOWN = 31231,
		};

		bool Validate(SpellEntry const * /*spellEntry*/) {
			return sSpellStore.LookupEntry(ROG_SPELL_CHEAT_DEATH_COOLDOWN);
		}

		bool Load() {
			absorbChance = SpellMgr::CalculateSpellEffectAmount(GetSpellProto(),
					EFFECT_0);
			return GetUnitOwner()->ToPlayer();
		}

		void CalculateAmount(AuraEffect const * /*aurEff*/, int32 & amount,
				bool & canBeRecalculated) {
			// Set absorbtion amount to unlimited
			amount = -1;
		}

		void Absorb(AuraEffect * aurEff, DamageInfo & dmgInfo,
				uint32 & absorbAmount) {
			Unit * target = GetTarget();
			if (dmgInfo.GetDamage() < target->GetHealth())
				return;
			if (target->ToPlayer()->HasSpellCooldown(
					ROG_SPELL_CHEAT_DEATH_COOLDOWN))
				return;
			if (!roll_chance_i(absorbChance))
				return;

			target->CastSpell(target, ROG_SPELL_CHEAT_DEATH_COOLDOWN, true);
			target->ToPlayer()->AddSpellCooldown(ROG_SPELL_CHEAT_DEATH_COOLDOWN,
					0, time(NULL) + 60);

			uint32 health10 = target->CountPctFromMaxHealth(10);

			// hp > 10% - absorb hp till 10%
			if (target->GetHealth() > health10)
				absorbAmount = dmgInfo.GetDamage() - target->GetHealth()
						+ health10;
			// hp lower than 10% - absorb everything
			else
				absorbAmount = dmgInfo.GetDamage();
		}

		void Register() {
			DoEffectCalcAmount +=
					AuraEffectCalcAmountFn(spell_rog_cheat_death_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
			OnEffectAbsorb +=
					AuraEffectAbsorbFn(spell_rog_cheat_death_AuraScript::Absorb, EFFECT_0);
		}
	};

	AuraScript *GetAuraScript() const {
		return new spell_rog_cheat_death_AuraScript();
	}
};

// 31130 - Nerves of Steel
class spell_rog_nerves_of_steel: public SpellScriptLoader {
public:
	spell_rog_nerves_of_steel() :
			SpellScriptLoader("spell_rog_nerves_of_steel") {
	}

	class spell_rog_nerves_of_steel_AuraScript: public AuraScript {
		PrepareAuraScript(spell_rog_nerves_of_steel_AuraScript)
		;

		uint32 absorbPct;

		bool Load() {
			absorbPct = SpellMgr::CalculateSpellEffectAmount(GetSpellProto(),
					EFFECT_0, GetCaster());
			return true;
		}

		void CalculateAmount(AuraEffect const * /*aurEff*/, int32 & amount,
				bool & canBeRecalculated) {
			// Set absorbtion amount to unlimited
			amount = -1;
		}

		void Absorb(AuraEffect * /*aurEff*/, DamageInfo & dmgInfo,
				uint32 & absorbAmount) {
			// reduces all damage taken while stun or fear
			if (GetTarget()->GetUInt32Value(UNIT_FIELD_FLAGS)
					& (UNIT_FLAG_STUNNED | UNIT_FLAG_FLEEING))
				absorbAmount = CalculatePctN(dmgInfo.GetDamage(), absorbPct);
		}

		void Register() {
			DoEffectCalcAmount +=
					AuraEffectCalcAmountFn(spell_rog_nerves_of_steel_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
			OnEffectAbsorb +=
					AuraEffectAbsorbFn(spell_rog_nerves_of_steel_AuraScript::Absorb, EFFECT_0);
		}
	};

	AuraScript *GetAuraScript() const {
		return new spell_rog_nerves_of_steel_AuraScript();
	}
};

class spell_rog_preparation: public SpellScriptLoader {
public:
	spell_rog_preparation() :
			SpellScriptLoader("spell_rog_preparation") {
	}

	class spell_rog_preparation_SpellScript: public SpellScript {
		PrepareSpellScript(spell_rog_preparation_SpellScript)
		bool Validate(SpellEntry const * /*spellEntry*/) {
			if (!sSpellStore.LookupEntry(ROGUE_SPELL_GLYPH_OF_PREPARATION))
				return false;
			return true;
		}

		void HandleDummy(SpellEffIndex /*effIndex*/) {
			Unit *caster = GetCaster();
			if (caster->GetTypeId() != TYPEID_PLAYER)
				return;

			//immediately finishes the cooldown on certain Rogue abilities
			const SpellCooldowns& cm =
					caster->ToPlayer()->GetSpellCooldownMap();
			for (SpellCooldowns::const_iterator itr = cm.begin();
					itr != cm.end();) {
				SpellEntry const *spellInfo = sSpellStore.LookupEntry(
						itr->first);

				if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE) {
					if (spellInfo->SpellFamilyFlags[1]
							& SPELLFAMILYFLAG1_ROGUE_COLDB_SHADOWSTEP || // Cold Blood, Shadowstep
							spellInfo->SpellFamilyFlags[0]
									& SPELLFAMILYFLAG_ROGUE_VAN_EVAS_SPRINT) // Vanish, Evasion, Sprint
						caster->ToPlayer()->RemoveSpellCooldown((itr++)->first,
								true);
					else if (caster->HasAura(
							ROGUE_SPELL_GLYPH_OF_PREPARATION)) {
						if (spellInfo->SpellFamilyFlags[1]
								& SPELLFAMILYFLAG1_ROGUE_DISMANTLE || // Dismantle
								spellInfo->SpellFamilyFlags[0]
										& SPELLFAMILYFLAG_ROGUE_KICK || // Kick
								(spellInfo->SpellFamilyFlags[0]
										& SPELLFAMILYFLAG_ROGUE_BLADE_FLURRY && // Blade Flurry
										spellInfo->SpellFamilyFlags[1]
												& SPELLFAMILYFLAG1_ROGUE_BLADE_FLURRY))
							caster->ToPlayer()->RemoveSpellCooldown(
									(itr++)->first, true);
						else
							++itr;
					} else
						++itr;
				} else
					++itr;
			}
		}

		void Register() {
			// add dummy effect spell handler to Preparation
			OnEffect +=
					SpellEffectFn(spell_rog_preparation_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
		}
	};

	SpellScript *GetSpellScript() const {
		return new spell_rog_preparation_SpellScript();
	}
};

// 51685-51689 Prey on the Weak
class spell_rog_prey_on_the_weak: public SpellScriptLoader {
public:
	spell_rog_prey_on_the_weak() :
			SpellScriptLoader("spell_rog_prey_on_the_weak") {
	}

	class spell_rog_prey_on_the_weak_AuraScript: public AuraScript {
		PrepareAuraScript(spell_rog_prey_on_the_weak_AuraScript)
		bool Validate(SpellEntry const * /*spellEntry*/) {
			if (!sSpellStore.LookupEntry(ROGUE_SPELL_PREY_ON_THE_WEAK))
				return false;
			return true;
		}

		void HandleEffectPeriodic(AuraEffect const * /*aurEff*/) {
			Unit* pTarget = GetTarget();
			Unit* pVictim = pTarget->getVictim();
			if (pVictim
					&& (pTarget->GetHealthPct() > pVictim->GetHealthPct())) {
				if (!pTarget->HasAura(ROGUE_SPELL_PREY_ON_THE_WEAK)) {
					int32 bp = SpellMgr::CalculateSpellEffectAmount(
							GetSpellProto(), 0);
					pTarget->CastCustomSpell(pTarget,
							ROGUE_SPELL_PREY_ON_THE_WEAK, &bp, 0, 0, true);
				}
			} else
				pTarget->RemoveAurasDueToSpell(ROGUE_SPELL_PREY_ON_THE_WEAK);
		}

		void Register() {
			OnEffectPeriodic +=
					AuraEffectPeriodicFn(spell_rog_prey_on_the_weak_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
		}
	};

	AuraScript *GetAuraScript() const {
		return new spell_rog_prey_on_the_weak_AuraScript();
	}
};

class spell_rog_shiv: public SpellScriptLoader {
public:
	spell_rog_shiv() :
			SpellScriptLoader("spell_rog_shiv") {
	}

	class spell_rog_shiv_SpellScript: public SpellScript {
		PrepareSpellScript(spell_rog_shiv_SpellScript)
		bool Validate(SpellEntry const * /*spellEntry*/) {
			if (!sSpellStore.LookupEntry(ROGUE_SPELL_SHIV_TRIGGERED))
				return false;
			return true;
		}

		void HandleDummy(SpellEffIndex /*effIndex*/) {
			Unit *caster = GetCaster();
			if (caster->GetTypeId() != TYPEID_PLAYER)
				return;

			if (Unit *unitTarget = GetHitUnit())
				caster->CastSpell(unitTarget, ROGUE_SPELL_SHIV_TRIGGERED, true);
		}

		void Register() {
			// add dummy effect spell handler to Shiv
			OnEffect +=
					SpellEffectFn(spell_rog_shiv_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
		}
	};

	SpellScript *GetSpellScript() const {
		return new spell_rog_shiv_SpellScript();
	}
};

class spell_rog_deadly_poison: public SpellScriptLoader {
public:
	spell_rog_deadly_poison() :
			SpellScriptLoader("spell_rog_deadly_poison") {
	}

	class spell_rog_deadly_poison_SpellScript: public SpellScript {
		PrepareSpellScript(spell_rog_deadly_poison_SpellScript)

	private:
		uint8 m_stackAmount;
	public:
		spell_rog_deadly_poison_SpellScript() :
				m_stackAmount(0) {
		}

		void HandleBeforeHit() {
			Player * player = GetCaster()->ToPlayer();
			Unit * target = GetHitUnit();

			if (!player || !target)
				return;

			// Deadly Poison
			if (AuraEffect const * aurEff = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_ROGUE, 0x10000, 0x80000, 0, player->GetGUID()))
				m_stackAmount = aurEff->GetBase()->GetStackAmount();
		}

		void HandleAfterHit() {
			if (m_stackAmount < 5)
				return;

			Player * player = GetCaster()->ToPlayer();
			Unit * target = GetHitUnit();
			Item * castItem = GetCastItem();

			if (!player || !target || !castItem)
				return;

			Item * item = player->GetItemByPos(INVENTORY_SLOT_BAG_0,
					EQUIPMENT_SLOT_MAINHAND);

			if (item == castItem)
				item = player->GetItemByPos(INVENTORY_SLOT_BAG_0,
						EQUIPMENT_SLOT_OFFHAND);

			if (!item)
				return;

			// item combat enchantments
			for (uint8 e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot) {
				uint32 enchant_id = item->GetEnchantmentId(
						EnchantmentSlot(e_slot));
				SpellItemEnchantmentEntry const * pEnchant =
						sSpellItemEnchantmentStore.LookupEntry(enchant_id);

				if (!pEnchant)
					continue;

				for (uint8 s = 0; s < 3; ++s) {
					if (pEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
						continue;

					SpellEntry const *spellInfo = sSpellStore.LookupEntry(
							pEnchant->spellid[s]);

					if (!spellInfo) {
						sLog->outError(
								"Player::CastItemCombatSpell Enchant %i, cast unknown spell %i",
								pEnchant->ID, pEnchant->spellid[s]);
						continue;
					}

					// Proc only rogue poisons
					if ((spellInfo->SpellFamilyName != SPELLFAMILY_ROGUE)
							|| (spellInfo->Dispel != DISPEL_POISON))
						continue;

					// Do not reproc deadly
					if (spellInfo->SpellFamilyFlags.IsEqual(0x10000, 0x80000,
							0))
						continue;

					if (IsPositiveSpell(pEnchant->spellid[s]))
						player->CastSpell(player, pEnchant->spellid[s], true,
								item);
					else
						player->CastSpell(target, pEnchant->spellid[s], true,
								item);
				}
			}
		}

		void Register() {
			BeforeHit +=
					SpellHitFn(spell_rog_deadly_poison_SpellScript::HandleBeforeHit);
			AfterHit +=
					SpellHitFn(spell_rog_deadly_poison_SpellScript::HandleAfterHit);
		}
	};

	SpellScript * GetSpellScript() const {
		return new spell_rog_deadly_poison_SpellScript();
	}
};

void AddSC_rogue_spell_scripts() {
	new spell_rog_cheat_death();
	new spell_rog_nerves_of_steel();
	new spell_rog_preparation();
	new spell_rog_prey_on_the_weak();
	new spell_rog_shiv();
	new spell_rog_deadly_poison();
}
